package zhongbo.javaweb.instagram.service;

import com.mongodb.client.MongoCollection;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.mongodb.core.DocumentCallbackHandler;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import zhongbo.javaweb.instagram.model.Config;
import zhongbo.javaweb.instagram.model.Task;
import zhongbo.javaweb.instagram.schedule.ProxyCheckSchedule;
import zhongbo.javaweb.instagram.schedule.UserCheckSchedule;
import zhongbo.javaweb.instagram.service.model.ConfigService;
import zhongbo.javaweb.instagram.service.model.TaskService;
import zhongbo.javaweb.instagram.service.model.UserService;
import zhongbo.javaweb.instagram.service.runner.InstagramProcessor;
import zhongbo.javaweb.instagram.service.runner.InstagramTask;
import zhongbo.javaweb.instagram.util.HttpClientUtil;

import javax.annotation.PostConstruct;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;

import static zhongbo.javaweb.instagram.util.PerformanceUtil.*;

/**
 * 说明： ins相关服务
 *
 * @author zhongboWork
 * @version 1.0
 * @since 2021-12-16 10:25
 */
@Service
@Slf4j
public class InstagramService {

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private TaskService taskService;

    @Autowired
    private ConfigService configService;

    @Autowired
    private UserService userService;

    @Autowired
    private ProxyCheckSchedule proxyCheckSchedule;

    @Autowired
    private UserCheckSchedule userCheckSchedule;

    @Value("${userProfileName}")
    private String userProfileName = "userProfile";

    @Value("${userPostName}")
    private String userPostName = "userPost";


    private MongoCollection<Document> userProfileCollection;
    private MongoCollection<Document> userPostCollection;
    private Queue<Document> userProfileList = new ConcurrentLinkedQueue<>();
    private static Map<String, InstagramTask> holdingTasks = new ConcurrentHashMap<>();
    private String googleBotAgent = "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)";

    @PostConstruct
    public void init() {
        countStart();
        String keyNamesCounter = "keyNamesCounter";
        countStart(keyNamesCounter);
        //Set<String> collectionNames = mongoTemplate.getCollectionNames();
        log.info("读取 collectionNames 用时: {}", countEndStr(keyNamesCounter));
        //checkCollections(collectionNames, Arrays.asList(userProfileName, userPostName));
        userProfileCollection = mongoTemplate.getCollection(userProfileName);
        userPostCollection = mongoTemplate.getCollection(userPostName);
        log.info("初始化中...");
        //loadUserProfile();
        log.info("初始化用时: {}", countEndStr());
    }

    private void loadUserProfile() {
        Criteria criteria = Criteria.where("is_private").is(false).and("fansCount").gt(1000).and("topics").in("Fashion", "Modeling", "Clothing & Outfits");
        Query query = Query.query(criteria);
        query.fields().include("id");
        String userProfileOne = userProfileName + "One";
        String userProfilePercents = userProfileName + "Percents";
        AtomicInteger i = new AtomicInteger();
        int logPercents = 1000;
        DocumentCallbackHandler dch = document -> {
            userProfileList.add(document);
            log.debug("load {}/{} cost {}", i.incrementAndGet(), userProfileList.size(), countEndStr(userProfileOne));
            countStart(userProfileOne);
            if (i.intValue() % logPercents == 0) {
                log.info("load total {}, {} percent cost {}, total cost {}", i.intValue(), logPercents, countEndStr(userProfilePercents), countMiddleStr(userProfileName));
                countStart(userProfilePercents);
            }
        };
        countStart(userProfileName);
        countStart(userProfileOne);
        countStart(userProfilePercents);
        userProfileList.clear();
        mongoTemplate.executeQuery(query, userProfileName, dch);
        log.info("load all userProfile {} cost {}", i.intValue(), countEndStr(userProfileName));
    }

    private void checkCollections(Set<String> collectionNames, List<String> checkList) {
        List<String> noList = new ArrayList<>();
        for (String collectionName : checkList) {
            if (!collectionNames.contains(collectionName)) {
                noList.add(collectionName);
            }
        }
        if (noList.size() > 0) {
            log.error("启动检查失败,没有collection: {}", noList);
            quit();
        }
    }

    public boolean start(String taskId) {
        InstagramTask instagramTask = holdingTasks.get(taskId);
        if (instagramTask == null) {
            instagramTask = buildInstagramTask(taskId);
        }
        if (!instagramTask.isRunning()) {
            instagramTask.start();
            holdingTasks.put(taskId, instagramTask);
        }
        return true;
    }

    public boolean stop(String taskId) {
        InstagramTask instagramTask = holdingTasks.get(taskId);
        if (instagramTask != null) {
            instagramTask.stop();
            return true;
        } else {
            Task task = taskService.findOne(taskId);
            if (task != null) {
                task.setRunning(false);
                task.setPaused(false);
                taskService.addOrUpdate(task);
                return true;
            }
        }
        return false;
    }

    public boolean pause(String taskId) {
        InstagramTask instagramTask = holdingTasks.get(taskId);
        if (instagramTask != null) {
            instagramTask.pause();
            return true;
        }
        return false;
    }

    public boolean resume(String taskId) {
        InstagramTask instagramTask = holdingTasks.get(taskId);
        if (instagramTask != null) {
            instagramTask.resume();
            return true;
        }
        return false;
    }

    private InstagramTask buildInstagramTask(String taskId) {
        Task task = taskService.findOne(taskId);
        if (task != null) {
            if (StringUtils.isEmpty(proxyCheckSchedule.getAvailableProxy())) {
                String msg = String.format("没有可用代理地址!任务创建失败! taskId=%s", taskId);
                throw new RuntimeException(msg);
            }
            if (userProfileList.isEmpty()) {
                loadUserProfile();
                if (userProfileList.isEmpty()) {
                    String msg = String.format("userProfileList列表为空!任务创建失败! taskId=%s", taskId);
                    throw new RuntimeException(msg);
                }
            }
            InstagramProcessor processor = new InstagramProcessor(mongoTemplate, userProfileCollection, userPostCollection, proxyCheckSchedule);
            processor.setBotGoogleUserAgent(googleBotAgent);

            String userCookie = getUserCookie();
            processor.setCookie(userCookie);
            if (log.isDebugEnabled()) {
                log.debug("使用cookie: {}", userCookie);
            }
            processor.setFirst(task.getFirst());
            processor.setTimeOutMs(task.getTimeoutExecTime());
            List<Config> configList = configService.findAll();
            int threadSize = task.getThreadSize();
            if (threadSize < 1) {
                threadSize = 1;
            }
            if (configList.isEmpty()) {
                String msg = "没有设置信息,请先配置Config数据";
                throw new RuntimeException(msg);
            }
            Config config = configList.get(0);
            long sleepTime = config.getSleepTimeMs();
            if (sleepTime > 0) {
                threadSize = 1;
                processor.setSleepTime(sleepTime);
            }
            return new InstagramTask(task.getTaskName(), threadSize, userProfileList, processor, task, taskService);
        } else {
            String msg = String.format("任务不存在! taskId=%s", taskId);
            throw new RuntimeException(msg);
        }
    }

    private String getUserCookie() {
        String userCookie = userCheckSchedule.getAvailableUser();
        if (StringUtils.isEmpty(userCookie)) {
            String msg = "错误的设置信息,用户 userCookie 为空!";
            throw new RuntimeException(msg);
        }
        return userCookie;
    }

    public String testGetIns(String url, String proxyUrl, Map<String, String> headers) throws Exception {
        log.info("测试下载 url: {} proxy: {} headers: {}", url, proxyUrl, headers);
        String availableProxy;
        String auto = "auto";
        if (auto.equals(proxyUrl)) {
            availableProxy = proxyCheckSchedule.getAvailableProxy();
            if (StringUtils.isEmpty(availableProxy)) {
                String msg = String.format("测试url: %s 失败! 无可用代理!", url);
                throw new RuntimeException(msg);
            }
        } else {
            availableProxy = proxyUrl;
        }
        RequestConfig requestConfig;
        int timeOutMs = 10000;
        if (!StringUtils.isEmpty(availableProxy)) {
            HttpHost proxy = HttpHost.create(availableProxy);
            requestConfig = RequestConfig.custom()
                    .setProxy(proxy)
                    .setConnectTimeout(timeOutMs)
                    .setRedirectsEnabled(false)
                    .build();
        } else {
            requestConfig = RequestConfig.custom()
                    .setConnectTimeout(timeOutMs)
                    .setRedirectsEnabled(false)
                    .build();
        }
        HttpGet httpGet = new HttpGet(url);
        httpGet.setConfig(requestConfig);
        if (headers != null && !headers.isEmpty()) {
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                String key = entry.getKey();
                String value = entry.getValue();
                httpGet.setHeader(key, value);
            }
        } else {
            httpGet.setHeader(HTTP.USER_AGENT, googleBotAgent);
        }
        CloseableHttpClient client = HttpClientUtil.chooseInsClient();
        CloseableHttpResponse execute = client.execute(httpGet);
        String body = EntityUtils.toString(execute.getEntity());
        log.debug("测试url: {} 成功! proxy: {} body: {}", url, availableProxy, body);
        return body;
    }

    /**
     * 结束程序运行
     */
    private void quit() {
        log.warn("程序退出");
        System.exit(0);
    }
}
